"""
Copyright 2009, Thomas Dejanovic, Jay Shurtz.
 
This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
"""

id = "$Id: hatch_property_propagator.py 718 2010-09-14 07:14:25Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/tags/taggle_release_2.3/hatch/hatch_visitors/hatch_property_propagator.py $
# $Author: jayshurtz $
version = " ".join(id.split()[1:3])


import to_hatch_structure
from hatch_visitor import *
from hatch_constants import PROPAGATED_ACCESS_MODES, PROPAGATED_ACCESS_EXPRESSIONS
from hatch_constants import PROPAGATE_FLAG, PROPAGATED_PROPERTIES


class HatchPropertyPropagator(HatchVisitor):
    """ Class for propagating specific properties of a hatch node into it's 
        childNodes.  This is done to make designing easier.
        This class implements all the property-propagation-related methods that 
        would otherwise be needed by the hatch node classes themselves.

        Note: This class should visit a design FIRST, to propagate the needed 
        properties into child Nodes.
    """
    def visit_function(self, hatchNode):
        """ Propagate specific hatch node properties into child nodes.
            'hatchNode' should be the top node in a hatch design.
        """
        for node in HatchNodeIteratorDepthFirst(hatchNode): # Recurse into child Nodes, having already propagated values into them.
            if len(node.__getattribute__(CHILDNODES_ATTRIBUTE)) > 0:
                self.propagate_access_values(node)
                self.propagate_properties(node)
                #XXX Add any new propagate functions here.

    def propagate_properties(self, hatchNode):
        """ Propagate user defined properties that are always propagated, do 
            not override child property if it already exists.
            'hatchNode' should be the top node in a hatch design.
        """
        for prop in PROPAGATED_PROPERTIES:
            if prop in hatchNode.properties:
                for c in hatchNode.childNodes:
                    if prop not in c.properties:    # Do not override child property.
                        c.properties[prop] = hatchNode.properties[prop]

    def propagate_access_values(self, hatchNode):
        """ Propagate access values into immediate child nodes.  Use inclusive
            OR to accumulate. Where not possible, prefer the childNode value.
            'hatchNode' should be the top node in a hatch design.
        """
        if hatchNode.properties.get(PROPAGATE_FLAG, False): # Exists and True.
            for accessType in PROPAGATED_ACCESS_MODES - set([PROPAGATE_FLAG]):  # Extra proptection against propagaing the propagate flag.
                try:
                    hatchNodeValues = set(hatchNode.properties[accessType]) # Use set type to throw out repeats.
                except KeyError, e: # Don't do dictionary lookup twice.
                    pass    # Node does not specify this property, skip this property.
                else:
                    for c in hatchNode.__getattribute__(CHILDNODES_ATTRIBUTE):
                        try:
                            childNodeValues = set(c.properties[accessType])
                        except KeyError, e:
                            c.properties[accessType] = ''.join(hatchNodeValues)    # child does not specify this property.
                        else:
                            c.properties[accessType] = ''.join(hatchNodeValues | childNodeValues)  # Set property to unified set.

            for relatedProp in PROPAGATED_ACCESS_EXPRESSIONS - set([PROPAGATE_FLAG]):    # Extra proptection against propagaing the propagate flag.
                if relatedProp in hatchNode.properties:
                    for c in hatchNode.__getattribute__(CHILDNODES_ATTRIBUTE):
                        if relatedProp not in c.properties: # Prefer value if already present.
                            c.properties[relatedProp] = hatchNode.properties[relatedProp]


# Main for test
if __name__ == "__main__":

    consoleHandler.setLevel(logging.DEBUG)

    pp = HatchPropertyPropagator()

    from hatch_nodes.hatch_node_physical import HatchNodePhysical
    h0 = HatchNodePhysical('h0', hw='s', trigger_count=0)
    h1 = HatchNodePhysical('h1', sw='w', trigger_count=1)
    h2 = HatchNodePhysical('h2', hw='s', trigger_count=3)
    h3 = HatchNodePhysical('h3', hw='s', auto_clear_on='ac_on', auto_set_on='as_on', auto_preset_on='ap_on')

    h0h1 = HatchNodePhysical('h0h1', h0, h1, hw='c', auto='t', auto_trigger_on='at_on')
    h2h0h1 = HatchNodePhysical('h2h0h1', h2, h0h1, hw='i', propagate=True)
    h3h2h0h1 = HatchNodePhysical('h3h2h0h1', h3, h2h0h1, hw='c', auto='t', auto_trigger_on='at_on_2', propagate=True)

    hatchNodes = [h0, h1, h2, h3, h0h1, h2h0h1, h3h2h0h1]

    for hatchNode in hatchNodes:
        pp.visit(hatchNode)
        print hatchNode.info(recurse=True) # Print properties.


